package crossCount;

import java.io.IOException;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Random;

import _general.Config;
import _general.geometry.Cross;
import _general.geometry.Edge;
import _general.geometry.Node;
import _general.geometry.Point;
import _general.lefttree.LeftTree;
import _general.problem.CrossCount;

public class GenerallyCrossCountLeftTree extends CrossCount {

	LeftTree<Point> pointSet;
	
	public GenerallyCrossCountLeftTree() {
		super();
		setName("Generally Cross Count with Left Tree");
	}

	@Override
	public void createInitialList(String preSort, int size,
			long randomSeed) throws Exception {
		
		//createInitialList(preSort, size);

		this.pointSet = LeftTree.getInstance(new Comparator<Point>() {
			private final double eps = (double) 0.0000001;
			@Override
			public int compare(Point arg0, Point arg1) {
				if (Math.abs(arg0.getX() - arg1.getX()) < eps && Math.abs(arg1.getY() - arg0.getY()) < eps )
					return 0;
				else
					return (int) Math.signum(arg0.getX() - arg1.getX());
			}
			
		});
		int rangeX = 800;
		int rangeY = 600;
		
		Edge nextEdge = null;
		Node startNode = null;
		Node endNode = null;
		
		if (preSort.toLowerCase().equals("random")){
			Random ran = new Random(randomSeed);
			for (int i = 0; i < size; i++) {
				
				startNode = new Node((ran.nextDouble() * rangeX), (ran.nextDouble() * rangeY));
				endNode   = new Node((ran.nextDouble() * rangeX), (ran.nextDouble() * rangeY));
				
				
				if (startNode.getX() < endNode.getX()) {
					nextEdge = new Edge(startNode, endNode);
					startNode.setEdge(nextEdge, true);
					endNode.setEdge(nextEdge, false);
				} else {
					nextEdge = new Edge(endNode, startNode);
					startNode.setEdge(nextEdge, false);
					endNode.setEdge(nextEdge, true);
				}
				
				this.pointSet.add(nextEdge.getStart());
				this.pointSet.add(nextEdge.getEnd());
			}
		}
	}
	

	@Override
	public void start() {
		while(!pointSet.isEmpty()) {
			Point p = pointSet.poll();

			
			if (p.getClass().equals(Node.class) && ((Node) p).isStart()){
				
				if (Config.log) System.out.printf("actual Node (%.0f, %.0f) is start Node\n", p.getX(), p.getY());
				
				activeEdge.sortedAdd(((Node) p).getEdge());
				
				Edge successor = activeEdge.getSuccessor(((Node) p).getEdge());
				Edge predecessor = activeEdge.getPredecessor(((Node) p).getEdge());
				
				if(successor != null && ((Node) p).getEdge().isIntersection(successor)){
					
					Cross newCross = ((Node) p).getEdge().getIntersection(successor);
					if(p.getX() < newCross.getX()) {
						if(Config.log) System.out.printf("actual Edge has an intersection with successor (%.0f, %.0f) -> (%.0f, %.0f)\n", successor.getStart().getX(), successor.getStart().getY(), successor.getEnd().getX(), successor.getEnd().getY());
						pointSet.add(newCross);
					}
				}
				
				if(predecessor != null && ((Node) p).getEdge().isIntersection(predecessor)){
					Cross newCross = predecessor.getIntersection(((Node) p).getEdge());
					if (p.getX() < newCross.getX()) {
						if(Config.log) System.out.printf("actual Edge has an intersection with predecessor (%.0f, %.0f) -> (%.0f, %.0f)\n", predecessor.getStart().getX(), predecessor.getStart().getY(), predecessor.getEnd().getX(), predecessor.getEnd().getY());
						pointSet.add(newCross);
					}
				}
				
			} else if (p.getClass().equals(Node.class) && !((Node) p).isStart()){
				
				if(Config.log) System.out.printf("actual Node (%.0f, %.0f) is end Node\n", p.getX(), p.getY());
				
				Edge successor = activeEdge.getSuccessor(((Node) p).getEdge());
				Edge predecessor = activeEdge.getPredecessor(((Node) p).getEdge());
				
				if(successor != null && predecessor != null && successor.isIntersection(predecessor)){
					Cross newCross = predecessor.getIntersection(successor);
					if(p.getX() < newCross.getX()) {
						if(Config.log) System.out.printf("predecessor (%.0f, %.0f) -> (%.0f, %.0f) has an intersection with successor (%.0f, %.0f) -> (%.0f, %.0f)\n", predecessor.getStart().getX(), predecessor.getStart().getY(), predecessor.getEnd().getX(), predecessor.getEnd().getY(), successor.getStart().getX(), successor.getStart().getY(), successor.getEnd().getX(), successor.getEnd().getY());
						pointSet.add(newCross);
					}
				}
				
				activeEdge.remove(((Node) p).getEdge());
				
			} else if (p.getClass().equals(Cross.class)) {
				
				if(Config.log) System.out.printf("actual Node (%.0f, %.0f) is intersection\n", p.getX(), p.getY());
				
				crossCount++;
				
				if(Config.log) System.out.println("CC is now " + crossCount);
//				swapListElements(activeEdge,
//								((Cross) p).getEdge1(),
//								((Cross) p).getEdge2());
				
				activeEdge.swap(((Cross) p).getEdge1(), ((Cross) p).getEdge2());
				
				Edge predecessor = activeEdge.getPredecessor(((Cross) p).getEdge2());
				
				if (Config.log) if (predecessor != null)
					System.out.printf("predecessor is (%.0f, %.0f) -> (%.0f, %.0f)\n", predecessor.getStart().getX(), predecessor.getStart().getY(), predecessor.getEnd().getX(), predecessor.getEnd().getY());
				else
					System.out.println("no predecessor");
				
				if(predecessor != null && ((Cross) p).getEdge2().isIntersection(predecessor)) {
					Cross newCross = predecessor.getIntersection(((Cross) p).getEdge2());
					if(p.getX() < newCross.getX()) {
						if(Config.log) {
							System.out.printf("edge 2 has an intersection with predecessor (%.0f, %.0f) -> (%.0f, %.0f) ", predecessor.getStart().getX(), predecessor.getStart().getY(), predecessor.getEnd().getX(), predecessor.getEnd().getY());
//							System.out.println("existing? " + pointSet.contains(newCross));
						}
						pointSet.add(newCross);
					}
				}
				
				Edge successor = activeEdge.getSuccessor(((Cross) p).getEdge1());
				
				if (Config.log) if (successor != null)
					System.out.printf("successor is (%.2f, %.2f) -> (%.2f, %.2f)\n",successor.getStart().getX(), successor.getStart().getY(), successor.getEnd().getX(), successor.getEnd().getY());
				else
					System.out.println("no successor");
				
				if(successor != null && ((Cross) p).getEdge1().isIntersection(successor)) {
					Cross newCross = ((Cross) p).getEdge1().getIntersection(successor);
					if(p.getX() < newCross.getX()) {
						if(Config.log) {
							System.out.printf("edge 1 has an intersection with successor (%.0f, %.0f) -> (%.0f, %.0f) ", successor.getStart().getX(), successor.getStart().getY(), successor.getEnd().getX(), successor.getEnd().getY());
							System.out.println("existing? " + pointSet.contains(newCross));
						}
						pointSet.add(newCross);
					}
				}
				
			} else {
				try {
					throw new IOException();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if (Config.log){ 
				System.out.println("actual pointset");
				for ( Iterator<Point> i = pointSet.iterator(); i.hasNext(); )
				{
					Point nextPoint = (Point) i.next();
					System.out.printf("(%.0f, %.0f), ", nextPoint.getX(), nextPoint.getY());
				}
				System.out.println();
			}
			/*
			System.out.println("Edge-list");
			for (Iterator i = activeEdge.iterator(); i.hasNext();) {
				Point nextPoint = ((Edge) i.next()).getStart();
				
				System.out.println("(" + nextPoint.getX() + ", " + nextPoint.getY() + "), ");
			}*/
		}
	}
}
